#pragma once

class IAudioPolationSmoother
{
public:
	virtual void Process(double* audio, int length) = 0;
};

class AudioPolationSmootherBuffered : public IAudioPolationSmoother
{
public:
	double* mBuffer;
	int mLength;
	int mPosition;

	double mAverange;
	double mAverangeNext;
	double mEnv;

	AudioPolationSmootherBuffered(int buffer)
	{
		mBuffer = new double[buffer];
		mLength = 0;
		mPosition = 0;
		mAverange = 0;
		mAverangeNext = 0;
		mEnv = 0;
	}

	~AudioPolationSmootherBuffered()
	{
		delete mBuffer;
	}

	void Process(double* audio, int length)
	{
		for (int i = 0; i < length; i++)
		{
			mBuffer[mPosition++ % length] = audio[i];

			audio[i] -= mAverange;
		}

		mAverange = mAverangeNext;

		// Calculate the next averange weight.
		mAverangeNext = 0;
		for (int i = length - 1; i >= 0; i--)
		{
			mAverangeNext += mBuffer[i];
		}
		mAverangeNext /= length;
	}
};

class AudioPolationSmoother : public IAudioPolationSmoother
{
public:
	const double ADJUST_SPEED = 0.00001;

	double mLengthMultiply;
	int mLength;
	int mPosition;

	double mAverange;
	double mAdjust;
	double mAdjustPointer;

	AudioPolationSmoother(int buffer)
	{
		mLength = buffer;
		mLengthMultiply = 1.0 / buffer;
		mPosition = 0;
		mAverange = 0;
		mAdjust = 0;
		mAdjustPointer = 0;
	}

	void Process(double* audio, int length)
	{
		double _mLengthMultiply = mLengthMultiply;
		int    _mLength = mLength;
		int    _mPosition = mPosition;

		double _mAverange = mAverange;
		double _mAdjust = mAdjust;
		double _mAdjustPointer = mAdjustPointer;
		for (int i = 0; i < length; i++)
		{
			double& value = audio[i];

			_mAverange += value * _mLengthMultiply;

			if (_mPosition++ > _mLength)
			{
				// Reset and start to adjust until next reset.
				_mPosition = 0;

				_mAdjust = _mAverange;

				_mAverange = 0;
			}

			value -= _mAdjustPointer;

			_mAdjustPointer += ((_mAdjust - _mAdjustPointer) * ADJUST_SPEED);
		}
		mPosition = _mPosition;

		mAverange = _mAverange;
		mAdjust = _mAdjust;
		mAdjustPointer = _mAdjustPointer;
	}
};